package com.getan.boot.common.utils;

import java.time.*;
import java.time.chrono.ChronoLocalDate;
import java.time.format.DateTimeFormatter;
import java.time.temporal.TemporalAccessor;
import java.util.Date;

/**
 * 日期相关工具类
 *
 * @author Bruce.Gong
 * @since 1.0.0-SNAPSHOT
 */
@SuppressWarnings("unused")
public class DateUtils {

    /**
     * 获取当前日期
     *
     * @return LocalDate
     */
    public static LocalDate today() {
        return LocalDate.now();
    }

    /**
     * 获取当前时间
     *
     * @return LocalDateTime
     */
    public static LocalDateTime now() {
        return LocalDateTime.now();
    }

    /**
     * 获取当前时间
     *
     * @return LocalTime
     */
    public static LocalTime nowTime() {
        return LocalTime.now();
    }

    /**
     * 获取指定日期
     *
     * @param year  年
     * @param month 月
     * @param day   日
     * @return LocalDate
     */
    public static LocalDate ofDate(int year, int month, int day) {
        return LocalDate.of(year, month, day);
    }

    /**
     * 获取指定时间
     *
     * @param hour   时
     * @param minute 分
     * @param second 秒
     * @return LocalTime
     */
    public static LocalTime ofTime(int hour, int minute, int second) {
        return LocalTime.of(hour, minute, second);
    }

    /**
     * 获取指定日期
     *
     * @param year   年
     * @param month  月
     * @param day    日
     * @param hour   时
     * @param minute 分
     * @param second 秒
     * @return LocalDateTime
     */
    public static LocalDateTime ofDateTime(int year, int month, int day, int hour, int minute, int second) {
        return LocalDateTime.of(year, month, day, hour, minute, second);
    }

    /**
     * 获取指定日期
     *
     * @param date 日期
     * @param time 时间
     * @return LocalDateTime
     */
    public static LocalDateTime ofDateTime(LocalDate date, LocalTime time) {
        return LocalDateTime.of(date, time);
    }

    /**
     * 判断 LocalDate、LocalDateTime、LocalTime 是否相等
     * <p>
     * 如果 date1 与 date2 有一个为 null，则不相等，两个都为 null，也不相等
     *
     * <pre>
     *     equals(null, null) = false
     *     equals(date, null) = false
     *     equals(null, date) = false
     * </pre>
     *
     * @param date1 日期
     * @param date2 日期
     * @return LocalDateTime
     */
    public static <T extends ChronoLocalDate> boolean equals(T date1, T date2) {
        return date1 != null && date2 != null && date1.isEqual(date2);
    }

    /**
     * 日期格式化 日期格式为：yyyy-MM-dd
     *
     * @param date 日期
     * @return 格式化后的日期
     */
    public static String format(LocalDate date) {
        return format(date, DateHelper.DATE_PATTERN);
    }

    /**
     * 日期格式化 日期格式为：yyyy-MM-dd HH:mm:ss
     *
     * @param date 日期
     * @return 格式化后的日期
     */
    public static String format(LocalDateTime date) {
        return format(date, DateHelper.DATE_TIME_PATTERN);
    }

    /**
     * 日期格式化 日期格式为：yyyy-MM-dd
     *
     * @param date 日期
     * @return 格式化后的日期
     */
    public static String format(LocalTime date) {
        return format(date, DateHelper.TIME_PATTERN);
    }

    /**
     * 日期格式化
     *
     * @param date    日期
     * @param pattern 格式，如：DateUtils.DATE_PATTERN
     * @return 格式化后的日期
     */
    public static String format(LocalDate date, String pattern) {
        return date == null ? "" : date.format(DateTimeFormatter.ofPattern(pattern));
    }

    /**
     * 日期格式化
     *
     * @param date    日期
     * @param pattern 格式，如：DateUtils.TIME_PATTERN
     * @return 格式化后的日期
     */
    public static String format(LocalTime date, String pattern) {
        return date == null ? "" : date.format(DateTimeFormatter.ofPattern(pattern));
    }

    /**
     * 日期格式化
     *
     * @param date    日期
     * @param pattern 格式，如：DateUtils.DATE_TIME_PATTERN
     * @return 格式化后的日期
     */
    public static String format(LocalDateTime date, String pattern) {
        return date == null ? "" : date.format(DateTimeFormatter.ofPattern(pattern));
    }

    /**
     * 日期格式化
     *
     * @param date    日期
     * @param pattern 格式，如：DateUtils.DATE_PATTERN
     * @return 格式化后的日期
     */
    public static String format(LocalDate date, DateTimeFormatter pattern) {
        return date == null ? "" : date.format(pattern);
    }

    /**
     * 日期格式化
     *
     * @param date    日期
     * @param pattern 格式，如：DateUtils.TIME_PATTERN
     * @return 格式化后的日期
     */
    public static String format(LocalTime date, DateTimeFormatter pattern) {
        return date == null ? "" : date.format(pattern);
    }

    /**
     * 日期格式化
     *
     * @param date    日期
     * @param pattern 格式，如：DateUtils.DATE_TIME_PATTERN
     * @return 格式化后的日期
     */
    public static String format(LocalDateTime date, DateTimeFormatter pattern) {
        return date == null ? "" : date.format(pattern);
    }

    /**
     * 日期格式化 日期格式为：yyyy-MM-dd HH:mm:ss
     *
     * @param date 日期
     * @return 格式化后的日期
     */
    public static LocalDateTime parseDateTime(String date) {
        return parseDateTime(date, DateHelper.DATE_TIME_PATTERN);
    }

    /**
     * 日期格式化 日期格式为：yyyy-MM-dd
     *
     * @param date 日期
     * @return 格式化后的日期
     */
    public static LocalDate parseDate(String date) {
        return parseDate(date, DateHelper.DATE_PATTERN);
    }

    /**
     * 日期格式化 日期格式为：yyyy-MM-dd
     *
     * @param date 日期
     * @return 格式化后的日期
     */
    public static LocalTime parseTime(String date) {
        return parseTime(date, DateHelper.TIME_PATTERN);
    }

    /**
     * 根据指定格式进行格式化
     *
     * @param date    日期字符串
     * @param pattern 日期格式
     * @return 格式化后的日期
     */
    public static LocalDate parseDate(String date, String pattern) {
        return LocalDate.parse(date, DateTimeFormatter.ofPattern(pattern));
    }

    /**
     * 根据指定格式进行格式化
     *
     * @param date    日期字符串
     * @param pattern 日期格式
     * @return 格式化后的日期
     */
    public static LocalTime parseTime(String date, String pattern) {
        return LocalTime.parse(date, DateTimeFormatter.ofPattern(pattern));
    }

    /**
     * 根据指定格式进行格式化
     *
     * @param date    日期字符串
     * @param pattern 日期格式
     * @return 格式化后的日期
     */
    public static LocalDateTime parseDateTime(String date, String pattern) {
        return LocalDateTime.parse(date, DateTimeFormatter.ofPattern(pattern));
    }

    /**
     * 获取当前时间戳
     *
     * @return 时间戳
     */
    public static long currentTimeMillis() {
        return toTimeMillis(Instant.now());
    }

    /**
     * 获取时间戳，如果日期为空，则返回 0
     *
     * @param date 日期
     * @return 时间戳
     */
    public static long toTimeMillis(LocalDateTime date) {
        return toTimeMillis(toInstant(date));
    }

    /**
     * 获取时间戳，如果日期为空，则返回 0
     *
     * @param date 日期
     * @return 时间戳
     */
    public static long toTimeMillis(LocalDate date) {
        return toTimeMillis(toInstant(date));
    }

    /**
     * 获取时间戳，如果日期为空，则返回 0
     *
     * @param date 日期
     * @return 时间戳
     */
    public static long toTimeMillis(Instant date) {
        return date == null ? 0 : date.toEpochMilli();
    }

    /**
     * 获取时间戳，如果日期为空，则返回空
     *
     * @param date 日期
     * @return Instant
     */
    public static Instant toInstant(LocalDate date) {
        return date == null ? null : toInstant(date.atStartOfDay());
    }

    /**
     * 获取时间戳，如果日期为空，则返回空
     *
     * @param date 日期
     * @return Instant
     */
    public static Instant toInstant(LocalDateTime date) {
        return toInstant(date, DateHelper.DEFAULT_ZONE);
    }

    /**
     * 获取时间戳，如果日期为空，则返回空
     *
     * @param date 日期
     * @return Instant
     */
    public static Instant toInstant(LocalDateTime date, ZoneOffset zoneOffset) {
        return date == null ? null : date.toInstant(zoneOffset);
    }

    /**
     * 获取时间戳，如果日期为空，则返回空
     *
     * @param date 日期
     * @return Instant
     */
    public static Instant toInstant(Date date) {
        return date == null ? null : date.toInstant();
    }

    /**
     * 根据时间戳获取日期，如果时间戳为空，则返回空
     *
     * @param timestamp 时间戳
     * @return Instant
     */
    public static Instant toInstant(Long timestamp) {
        return timestamp == null ? null : Instant.ofEpochMilli(timestamp);
    }

    /**
     * 根据时间戳获取日期，如果时间戳为空，则返回空
     *
     * @param timestamp 时间戳
     * @return LocalDateTime
     */
    public static LocalDateTime toLocalDateTime(Long timestamp) {
        return toLocalDateTime(toInstant(timestamp));
    }

    /**
     * 根据时间戳获取日期，如果时间戳为空，则返回空
     *
     * @param timestamp 时间戳
     * @return LocalDateTime
     */
    public static LocalDateTime toLocalDateTime(Instant timestamp) {
        return timestamp == null ? null : timestamp.atZone(DateHelper.DEFAULT_ZONE).toLocalDateTime();
    }

    /**
     * java.util.Date 转换为 LocalDateTime
     *
     * @param date 时间
     * @return LocalDateTime
     */
    public static LocalDateTime toLocalDateTime(Date date) {
        return toLocalDateTime(toInstant(date));
    }

    /**
     * LocalDateTime 转换为 java.util.Date
     *
     * @param date LocalDateTime
     * @return Date
     */
    public static Date toDate(Instant date) {
        return date == null ? null : Date.from(date);
    }

    /**
     * LocalDateTime 转换为 java.util.Date
     *
     * @param date LocalDateTime
     * @return Date
     */
    public static Date toDate(LocalDateTime date) {
        return toDate(toInstant(date));
    }

    /**
     * 获取当天的 0 点
     *
     * @param date 日期
     * @return LocalDateTime
     */
    public static LocalDateTime atStartOfDay(LocalDate date) {
        return date == null ? null : date.atStartOfDay();
    }

    /**
     * 获取当天的 0 点
     *
     * @param date 日期
     * @return LocalDateTime
     */
    public static LocalDateTime atStartOfDay(LocalDateTime date) {
        return date == null ? null : date.toLocalDate().atStartOfDay();
    }

    /**
     * 获取当天的 23点59分59秒
     *
     * @param date 日期
     * @return LocalDateTime
     */
    public static LocalDateTime atLastOfDay(LocalDate date) {
        return date == null ? null : date.atStartOfDay().plusDays(1).minusSeconds(1);
    }

    /**
     * 获取当天的 23点59分59秒
     *
     * @param date 日期
     * @return LocalDateTime
     */
    public static LocalDateTime atLastOfDay(LocalDateTime date) {
        return date == null ? null : date.toLocalDate().atStartOfDay().plusDays(1).minusSeconds(1);
    }

    /**
     * 获取当月第一天
     *
     * @param date 日期
     * @return LocalDateTime
     */
    public static LocalDate startDayOfMonth(LocalDateTime date) {
        return date == null ? null : startDayOfMonth(date.toLocalDate());
    }

    /**
     * 获取当月第一天
     *
     * @param date 日期
     * @return LocalDateTime
     */
    public static LocalDate startDayOfMonth(LocalDate date) {
        return date == null ? null : date.withDayOfMonth(1);
    }

    /**
     * 获取当月最后一天
     *
     * @param date 日期
     * @return LocalDateTime
     */
    public static LocalDate lastDayOfMonth(LocalDateTime date) {
        return date == null ? null : lastDayOfMonth(date.toLocalDate());
    }

    /**
     * 获取当月最后一天
     *
     * @param date 日期
     * @return LocalDateTime
     */
    public static LocalDate lastDayOfMonth(LocalDate date) {
        return date == null ? null : date.withDayOfMonth(1).plusMonths(1).minusDays(1);
    }

    /**
     * 检查闰年，date 必须存在
     *
     * @return true | false
     */
    public static boolean isLeapYear(LocalDateTime date) {
        return date.toLocalDate().isLeapYear();
    }

    /**
     * 检查闰年，date 必须存在
     *
     * @return true | false
     */
    public static boolean isLeapYear(LocalDate date) {
        return date.isLeapYear();
    }

    /**
     * 计算两个日期之间的相差的天数，使用每天的 0 点进行计算，而不是当前时间，两个日期无所谓先后。
     * 两个入参必须不能为空，否则会抛出异常
     *
     * @param d1 日期
     * @param d2 日期
     * @return days
     * @throws IllegalArgumentException d1 == null || d2 == null
     */
    public static long diffDays(LocalDateTime d1, LocalDateTime d2) {
        Duration between = Duration.between(d1, d2);
        return Math.abs(between.toDays());
    }

    /**
     * 计算两个日期之间的相差的天数，两个日期无所谓先后。
     * 两个入参必须不能为空，否则会抛出异常
     *
     * @param d1 日期
     * @param d2 日期
     * @return days
     * @throws IllegalArgumentException d1 == null || d2 == null
     */
    public static long diffDays(LocalDate d1, LocalDate d2) {
        return Math.abs(d1.toEpochDay() - d2.toEpochDay());
    }

    /**
     * 获取几月几号的信息
     *
     * @param month 月
     * @param day   号
     * @return MonthDay
     */
    public static MonthDay toMonthDay(Integer month, Integer day) {
        return month == null || day == null ? null : MonthDay.of(month, day);
    }

    /**
     * 获取几月几号的信息
     *
     * @param date 日期
     * @return MonthDay
     */
    public static MonthDay toMonthDay(TemporalAccessor date) {
        return date == null ? null : MonthDay.from(date);
    }

    /**
     * 判断当前日期是否几月几号
     *
     * @param date  日期
     * @param month 月
     * @param day   日
     * @return true | false
     */
    public static boolean isMonthDay(LocalDate date, Integer month, Integer day) {
        return date != null && month != null && day != null && isMonthDay(date, MonthDay.of(month, day));
    }

    /**
     * 判断当前日期是否几月几号
     *
     * @param date     日期
     * @param monthDay 几月几号
     * @return true | false
     */
    public static boolean isMonthDay(LocalDate date, MonthDay monthDay) {
        return date != null && monthDay != null && monthDay.equals(MonthDay.from(date));
    }

    /**
     * 获取年份月份信息
     *
     * @param date 日期
     * @return YearMonth
     */
    public static YearMonth toYearMonth(TemporalAccessor date) {
        return date == null ? null : YearMonth.from(date);
    }

    /**
     * 获取当前日期是当月的第几天
     *
     * @param date 日期
     * @return YearMonth
     */
    public static Integer getMonthDay(TemporalAccessor date) {
        return date == null ? 0 : toYearMonth(date).lengthOfMonth();
    }

    /**
     * 获取两个日期之前的
     *
     * @param date1 日期
     * @param date2 日期
     * @return YearMonth
     */
    public static Duration between(LocalDateTime date1, LocalDateTime date2) {
        return date1 == null || date2 == null ? Duration.ofMillis(0) : Duration.between(date1, date2);
    }

}
